#include "stdint.h"
#include "bs_app.h"
#include "inner_flash.h"
#define LOG_TAG        "[flash level]"
#include "log.h"

/*!
    \brief 每次上电都从头寻找一个有效条目
    \param[in]  none
    \param[out] none
    \retval
*/
__attribute__((unused))
static int flash_level_init(wear_level_flash_t *wl)
{
    // 初始化一些常量
    wl->page_size = UPDATE_REGION_PAGE_SIZE;
    wl->entry_size = UPDATE_REGION_ENTRY_SIZE;
    wl->st_page = INNR_UPDATE_REGION_STADDR;
    wl->page_nr = 2;
    wl->end_page = wl->st_page + (wl->page_nr-1)*wl->page_size;

    // 扇区的最后一个条目的地址
    wl->last_entry = __ROUNDDOWN(wl->page_size-1, wl->entry_size);

    flash_level_find_entry(wl);
    // 此时 wl->cur_addr 地址必须是有效的
    return 0;
}


/*!
    \brief 验证当前条目是否有效
    \param[in]  none
    \param[out] none
    \retval FF则为0，校验通过则为1，无效数据则为2
*/
static uint32_t check_entry_valid(void *pdata)
{
    uint32_t *ptr = (uint32_t *)pdata;
    uint32_t temp = 0xFFFFFFFF;
    if (*ptr++ == temp && *ptr++ == temp && *ptr++ == temp && *ptr++ == temp) {
        return 0;
    }
    ptr = (uint32_t *)pdata;
    if (ptr[0] == ~ptr[1] && ptr[2] == ~ptr[3]) {
        return 1;
    }
    return 2;
}


/*!
    \brief 搜索一个扇区的空白项
    \param[in]  addr: 起始地址，必须按照扇区对齐
    \param[out] none
    \retval 0：find successful，1：全部是合法数据，2：不合法条目
*/
static uint32_t flash_level_search_blank_entry(wear_level_flash_t *wl, uint32_t *addr)
{
    uint32_t       entry_buf[32];
    uint32_t       retVal = 0;
    uint32_t       cur_addr = wl->cur_addr;
    uint32_t       max_items = wl->page_size / wl->entry_size;
    // for (int i=0; i<100; i++)
    // {
    //     inn_flash_read_32b(cur_addr, wl->entry_size, entry_buf);
	// 		  cur_addr += 16;
    // }
#if 0

    for(int i = 0; i<1024; i++) {
        inn_flash_read_32b(cur_addr, wl->entry_size, entry_buf);
        retVal = check_entry_valid(entry_buf);
        if (retVal == 0) {
            // return 0;
        } else {
            // TODO 可能非法数据
            __NOP();
        }
        cur_addr += wl->entry_size;
        if (cur_addr % wl->page_size == 0) {
            NOP;
        }
    }
#else
    uint32_t left,mid,right;
    left = 0;
    right = max_items;

    while(left < right) {
        mid = left + (right-left)/2;
        cur_addr = wl->cur_addr + mid*wl->entry_size;
        inn_flash_read_32b(cur_addr, wl->entry_size, entry_buf);
        retVal = check_entry_valid(entry_buf);
        if (retVal == 0) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    *addr = wl->cur_addr + right*wl->entry_size;

#endif
    return 0;
}


/*!
    \brief 重置：擦除第一个扇区，然后写入第一个条目
    \param[in]  none
    \param[out] none
    \retval
*/
int flash_level_reset(wear_level_flash_t *wl)
{
    // 擦除两个扇区
    inn_flash_eraser_sector(INNR_UPDATE_REGION_STADDR);
    inn_flash_eraser_sector(INNR_UPDATE_REGION_STADDR+UPDATE_REGION_PAGE_SIZE);

    // 重置索引，
    wl->cur_addr = wl->st_page;
    return 0;

}


/*!
    \brief 上电的时候才调用一次
    \brief 如果两个扇区都是空的，会返回最后一个扇区的最后一条
    \param[in]  none
    \param[out] none
    \retval
*/
int flash_level_find_entry(wear_level_flash_t *wl)
{
    uint8_t          read_buf[UPDATE_REGION_ENTRY_SIZE] = {0};
    uint32_t         i, find_ok, prev_page, blank_addr, prev_entry;
    uint32_t         addr;

    // 首先确定空白项位于哪个扇区：假定只有两个扇区，只要出现 FF...5A 的有可能扇区损坏
    // 将 FF的项编码为 bit_0，反码的项编码为 bit_1, 每一种组合都有可能是异常状态
    // 0. FF...FF | FF...FF     0     合法 刚初始化
    // 0. FF...FF | FF...5A     ...
    // 0. FF...FF | 5A...FF          不合法，但理论上不存在
    // 0. FF...FF | 5A...5A     0     合法
    // 0. FF...5A | FF...FF     ...
    // 0. FF...5A | FF...5A     ...
    // 0. FF...5A | 5A...FF     ...
    // 0. FF...5A | 5A...5A     ...    有一个扇区损坏？

    // 0. 5A...FF | FF...FF     0      合法：仅在第一次出现
    // 0. 5A...FF | FF...5A     ...
    // 0. 5A...FF | 5A...FF     ...
    // 0. 5A...FF | 5A...5A     0      合法
    // 0. 5A...5A | FF...FF     1      合法：0号扇区填满
    // 0. 5A...5A | FF...5A     ...    有一个扇区损坏？
    // 0. 5A...5A | 5A...FF     1      合法：
    // 0. 5A...5A | 5A...5A     ...    两个扇区都损坏？
    
    find_ok = 0;
    for (i=0; i<wl->page_nr; i++) {
        // 比较每个扇区的最后一项
        wl->cur_addr = wl->st_page + i*wl->page_size;
        addr = wl->cur_addr + wl->last_entry;
        inn_flash_read_32b(addr, wl->entry_size, (uint32_t *)read_buf);
        if ( check_entry_valid(read_buf) == 0) {
            find_ok = 1;
            break;
        }
    }

    if (!find_ok) {
        goto search_error;
    }

    prev_page = (wl->cur_addr == wl->st_page) ? wl->end_page : wl->cur_addr - wl->page_size;
    // 这里只找到第一条FF，如果全是FF，则返回第一条，如果全部无效，则返回??
    if (flash_level_search_blank_entry(wl, &blank_addr) == 0) {
        if (blank_addr % wl->page_size == 0) {
            prev_entry = prev_page + wl->last_entry;
        } else {
            prev_entry = blank_addr - wl->entry_size;
        }
        // 检查当前条目是否有效：理论上是有效的
        inn_flash_read_32b(prev_entry, wl->entry_size, wl->u.buf);
        if (check_entry_valid(wl->u.buf) != 1) {
            LOGE("current entry data is invalid");
            goto search_error;
        } else {
            //LOGI("current entry data is valid");
        }
    }
// search_success:
    wl->cur_addr = blank_addr;
	return 0;

search_error:
    LOGE("no find blank area in falsh");
    return 1;
}


/*
如果写入失败，就尝试下一条，
有可能擦除失败，也有可能写入失败，那么，你甚至不知道它原本是什么，跳到下一条，直到成功；那么紧接着的不一定是 0xFF,
那么读取的时候怎么找到合法的数据，
*/

/*!
    \brief 先跳到下一项，然后再切换
    \param[in]  none
    \param[out] none
    \retval
*/

int flash_level_write_entry(wear_level_flash_t *wl)
{
    uint32_t       read_buf[16];
    uint32_t       *write_buf = wl->u.buf;
    uint32_t       next_page, cur_entry;
    uint32_t       i, repeat_times, max_repeat;
    uint32_t       write_ok;

    max_repeat = wl->page_size / wl->entry_size;
    repeat_times = 0;
    // round(cur_addr - st_page, page_size) %  向下取整

    // write_buf[0] = used_cnt;
    write_buf[1] = ~write_buf[0];
    // write_buf[2] = ok_cnt;
    write_buf[3] = ~write_buf[2];

    cur_entry = wl->cur_addr;

write_entry_retry:
    if (cur_entry % wl->page_size == wl->last_entry) {
        // TODO 对齐
        next_page = (cur_entry - wl->st_page) / wl->page_size + 1;
        next_page = wl->st_page + (next_page % wl->page_nr) * wl->page_size;
        if (inn_flash_eraser_sector(next_page) != 0) {
            LOGE("inner falsh eraser failed");
            return -1;
        }
    }
    
    if(inn_flash_write_32b(cur_entry, wl->entry_size, write_buf) == 0) {
        inn_flash_read_32b(cur_entry, wl->entry_size, read_buf);
        for(i=0; i<wl->entry_size/4; i++) {
            if(read_buf[i] != write_buf[i]) goto write_error;
        }
        write_ok = 1;
    }

write_error:
    // 切换到下一个条目
    if (cur_entry % wl->page_size == wl->last_entry) {
        cur_entry = next_page;
    } else {
        cur_entry += wl->entry_size;
    }

    if(write_ok) goto write_success;
    repeat_times++;
    LOGI("inner falsh write entry failed, retry: %d", repeat_times);
    if (repeat_times == max_repeat) {
        LOGE("inner falsh write entry failed, retry failed.");
    }
    goto write_entry_retry;

write_success:
    wl->cur_addr = cur_entry;
    return 0;
}


/*!
    \brief 根据当前地址读取一条
    \brief 如果两个扇区都是空的，会返回最后一个扇区的最后一条
    \param[in]  none
    \param[out] none
    \retval
*/
int flash_level_read_entry(wear_level_flash_t *wl)
{
    uint32_t         cur_entry,prev_page, prev_entry;
    cur_entry = wl->cur_addr;
    if (cur_entry % wl->page_size == 0) {
        prev_page = (cur_entry == wl->st_page) ? wl->end_page : cur_entry - wl->page_size;
        prev_entry = prev_page + wl->last_entry;
    } else {
        prev_entry = cur_entry - wl->entry_size;
    }

    inn_flash_read_32b(prev_entry, wl->entry_size, wl->u.buf);
    if (check_entry_valid(wl->u.buf) != 1) {
        LOGE("read current entry data is invalid");
        // TODO 异常处理
    }
    return 0;
}
